package com.ruoyi.system.service.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysTokenService;

/**
 * Token服务业务层处理
 * 
 * @author ruoyi
 * @date 2024-01-01
 */
@Service
public class SysTokenServiceImpl implements ISysTokenService 
{
    private static final Logger log = LoggerFactory.getLogger(SysTokenServiceImpl.class);
    
    private static final String TOKEN_CACHE_PREFIX = "sys_token:";
    private static final int TOKEN_EXPIRE_MINUTES = 30;

    private final RestTemplate restTemplate = new RestTemplate();
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    @Autowired
    private RedisCache redisCache;

    /**
     * 从指定API获取Token
     * 
     * @param username 用户名
     * @param password 密码
     * @param apiUrl 接口地址
     * @return Token字符串
     * @throws Exception 获取失败时抛出异常
     */
    @Override
    public String getTokenFromApi(String username, String password, String apiUrl) throws Exception
    {
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password) || StringUtils.isEmpty(apiUrl))
        {
            throw new IllegalArgumentException("用户名、密码和接口地址不能为空");
        }

        try
        {
            // 构建请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);

            // 构建请求体
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("userName", username);  // 修改为userName，与外部API保持一致
            requestBody.put("password", password);

            HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);

            log.info("正在调用外部API获取Token，URL: {}", apiUrl);

            // 发送POST请求
            ResponseEntity<String> response = restTemplate.exchange(
                apiUrl, 
                HttpMethod.POST, 
                requestEntity, 
                String.class
            );

            if (response.getStatusCode().is2xxSuccessful())
            {
                // 首先尝试从响应头中获取token
                HttpHeaders responseHeaders = response.getHeaders();
                log.info("API调用成功，响应状态码: {}", response.getStatusCode());
                log.info("响应头信息: {}", responseHeaders);
                
                // 常见的token响应头字段名
                String[] tokenHeaderFields = {"token", "Token", "Authorization", "X-Token", "Access-Token", "Auth-Token"};
                
                for (String headerField : tokenHeaderFields)
                {
                    String headerValue = responseHeaders.getFirst(headerField);
                    if (StringUtils.isNotEmpty(headerValue))
                    {
                        log.info("从响应头成功提取Token，字段名: {}", headerField);
                        return headerValue;
                    }
                }
                
                // 如果响应头中没有找到token，再尝试从响应体中解析
                String responseBody = response.getBody();
                if (StringUtils.isNotEmpty(responseBody))
                {
                    try
                    {
                        JsonNode jsonNode = objectMapper.readTree(responseBody);
                        
                        // 常见的token字段名
                        String[] tokenFields = {"token", "access_token", "accessToken", "authToken", "jwt"};
                        
                        for (String field : tokenFields)
                        {
                            if (jsonNode.has(field))
                            {
                                String token = jsonNode.get(field).asText();
                                if (StringUtils.isNotEmpty(token))
                                {
                                    log.info("从响应体成功提取Token，字段名: {}", field);
                                    return token;
                                }
                            }
                        }
                        
                        // 如果没有找到标准字段，尝试从data字段中查找
                        if (jsonNode.has("data"))
                        {
                            JsonNode dataNode = jsonNode.get("data");
                            for (String field : tokenFields)
                            {
                                if (dataNode.has(field))
                                {
                                    String token = dataNode.get(field).asText();
                                    if (StringUtils.isNotEmpty(token))
                                    {
                                        log.info("从data字段成功提取Token，字段名: {}", field);
                                        return token;
                                    }
                                }
                            }
                        }
                        
                        // 如果都没找到，返回整个响应体
                        log.warn("未找到标准token字段，返回完整响应");
                        return responseBody;
                    }
                    catch (Exception e)
                    {
                        log.warn("解析JSON响应失败，返回原始响应: {}", e.getMessage());
                        return responseBody;
                    }
                }
                else
                {
                    throw new RuntimeException("API响应为空");
                }
            }
            else
            {
                throw new RuntimeException("API调用失败，状态码: " + response.getStatusCode() + 
                                         ", 响应: " + response.getBody());
            }
        }
        catch (Exception e)
        {
            log.error("调用外部API获取Token失败", e);
            throw new RuntimeException("调用外部API失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 从指定API获取Token并缓存到Redis
     * 
     * @param username 用户名
     * @param password 密码
     * @param apiUrl 接口地址
     * @return 包含token和过期时间的Map
     * @throws Exception 获取失败时抛出异常
     */
    @Override
    public Map<String, Object> getTokenWithCache(String username, String password, String apiUrl) throws Exception
    {
        // 先检查缓存中是否有有效的token
        Map<String, Object> cachedToken = getCachedToken(username, apiUrl);
        if (cachedToken != null)
        {
            log.info("从缓存中获取到有效token，用户: {}", username);
            return cachedToken;
        }
        
        // 缓存中没有，调用API获取新token
        String token = getTokenFromApi(username, password, apiUrl);
        
        // 将token缓存到Redis，设置30分钟过期
        String cacheKey = generateCacheKey(username, apiUrl);
        redisCache.setCacheObject(cacheKey, token, TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES);
        
        // 计算过期时间戳（当前时间 + 30分钟）
        long expireTime = System.currentTimeMillis() + (TOKEN_EXPIRE_MINUTES * 60 * 1000);
        
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("expireTime", expireTime);
        result.put("expireMinutes", TOKEN_EXPIRE_MINUTES);
        
        log.info("Token已缓存到Redis，用户: {}，过期时间: {} 分钟", username, TOKEN_EXPIRE_MINUTES);
        
        return result;
    }
    
    /**
     * 从Redis获取缓存的Token信息
     * 
     * @param username 用户名
     * @param apiUrl 接口地址
     * @return 包含token和剩余过期时间的Map，如果不存在则返回null
     */
    @Override
    public Map<String, Object> getCachedToken(String username, String apiUrl)
    {
        String cacheKey = generateCacheKey(username, apiUrl);
        String token = redisCache.getCacheObject(cacheKey);
        
        if (StringUtils.isNotEmpty(token))
        {
            // 获取剩余过期时间（秒）
            long remainingSeconds = redisCache.getExpire(cacheKey);
            
            if (remainingSeconds > 0)
            {
                // 计算过期时间戳
                long expireTime = System.currentTimeMillis() + (remainingSeconds * 1000);
                
                Map<String, Object> result = new HashMap<>();
                result.put("token", token);
                result.put("expireTime", expireTime);
                result.put("remainingSeconds", remainingSeconds);
                
                return result;
            }
        }
        
        return null;
    }
    
    /**
     * 生成缓存key
     * 
     * @param username 用户名
     * @param apiUrl 接口地址
     * @return 缓存key
     */
    private String generateCacheKey(String username, String apiUrl)
    {
        // 使用用户名和API地址的hash值作为缓存key的一部分，避免key过长
        return TOKEN_CACHE_PREFIX + username + ":" + apiUrl.hashCode();
    }
    
    /**
     * 清空所有Token缓存
     * 
     * @return 清空结果
     */
    @Override
    public boolean clearAllTokenCache()
    {
        try
        {
            // 获取所有以TOKEN_CACHE_PREFIX开头的key
            redisCache.deleteObject(TOKEN_CACHE_PREFIX + "*");
            log.info("已清空所有Token缓存");
            return true;
        }
        catch (Exception e)
        {
            log.error("清空Token缓存失败", e);
            return false;
        }
    }
    
    /**
     * 清空指定用户的Token缓存
     * 
     * @param username 用户名
     * @param apiUrl 接口地址
     * @return 清空结果
     */
    @Override
    public boolean clearTokenCache(String username, String apiUrl)
    {
        try
        {
            String cacheKey = generateCacheKey(username, apiUrl);
            redisCache.deleteObject(cacheKey);
            log.info("已清空用户 {} 的Token缓存", username);
            return true;
        }
        catch (Exception e)
        {
            log.error("清空用户 {} 的Token缓存失败", username, e);
            return false;
        }
    }
    
    /**
     * 重新获取Token并刷新缓存
     * 
     * @param username 用户名
     * @param password 密码
     * @param apiUrl 接口地址
     * @return 包含新token和过期时间的Map
     * @throws Exception 获取失败时抛出异常
     */
    @Override
    public Map<String, Object> refreshTokenCache(String username, String password, String apiUrl) throws Exception
    {
        // 先清空现有缓存
        clearTokenCache(username, apiUrl);
        
        // 重新获取并缓存token
        String token = getTokenFromApi(username, password, apiUrl);
        
        // 将token缓存到Redis，设置30分钟过期
        String cacheKey = generateCacheKey(username, apiUrl);
        redisCache.setCacheObject(cacheKey, token, TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES);
        
        // 计算过期时间戳（当前时间 + 30分钟）
        long expireTime = System.currentTimeMillis() + (TOKEN_EXPIRE_MINUTES * 60 * 1000);
        
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("expireTime", expireTime);
        result.put("expireMinutes", TOKEN_EXPIRE_MINUTES);
        
        log.info("Token已重新获取并刷新缓存，用户: {}，过期时间: {} 分钟", username, TOKEN_EXPIRE_MINUTES);
        
        return result;
    }
}